package org.unidata.mdm.meta.service.impl.data.instance;

import java.util.Date;
import java.util.List;
import java.util.Objects;

import org.unidata.mdm.core.context.DataRecordContext;
import org.unidata.mdm.core.type.model.EntityElement;
import org.unidata.mdm.core.type.model.GeneratingElement;
import org.unidata.mdm.core.type.model.GenerationStrategyType;
import org.unidata.mdm.core.type.model.ValidityPeriodElement;
import org.unidata.mdm.core.type.model.support.ExternalIdValueGenerator;
import org.unidata.mdm.meta.service.impl.instance.ValidityPeriodImpl;
import org.unidata.mdm.meta.type.model.ValueGenerationStrategy;
import org.unidata.mdm.meta.type.model.entities.AbstractEntity;
import org.unidata.mdm.meta.type.model.entities.Entity;
import org.unidata.mdm.meta.type.model.entities.LookupEntity;
import org.unidata.mdm.meta.type.model.entities.NestedEntity;
import org.unidata.mdm.meta.type.model.strategy.CustomValueGenerationStrategy;
import org.unidata.mdm.meta.util.ValueGeneratingUtils;

/**
 * @author Mikhail Mikhailov on May 18, 2020
 */
public abstract class AbstractEntityImpl<X extends AbstractEntity<?>> extends AbstractAttributesImpl<X>
    implements GeneratingElement, ValidityPeriodElement {
    /**
     * Value generator.
     */
    protected final ExternalIdValueGenerator generator;
    /**
     * Validity period settings.
     */
    protected final ValidityPeriodElement validityPeriod;
    /**
     * Generation strategy type.
     */
    protected final GenerationStrategyType generationStrategyType;
    /**
     * Constructor.
     * @param attrs
     * @param bvtMap
     */
    protected AbstractEntityImpl(X entity, List<NestedEntity> nested) {
        super(entity, nested);

        // So far, only registers and lookups can have generating elements
        ValueGenerationStrategy s = null;
        if (entity instanceof LookupEntity) {
            s = ((LookupEntity) entity).getExternalIdGenerationStrategy();
        } else if (entity instanceof Entity) {
            s = ((Entity) entity).getExternalIdGenerationStrategy();
        }

        this.generationStrategyType = getStrategyType(s);
        this.generator = getGenerator(s);
        this.validityPeriod = new ValidityPeriodImpl(entity.getValidityPeriod());
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Object generate(DataRecordContext input) {
        return generator.generate((EntityElement) this, input);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public GenerationStrategyType getStrategyType() {
        return generationStrategyType;
    }
    /*
     * Return guessed generation type.
     * @return type or null
     */
    private GenerationStrategyType getStrategyType(ValueGenerationStrategy s) {

        if (Objects.nonNull(s)) {
            switch (s.getStrategyType()) {
                case CONCAT:
                    return GenerationStrategyType.CONCAT;
                case CUSTOM:
                    return GenerationStrategyType.CUSTOM;
                case RANDOM:
                    return GenerationStrategyType.RANDOM;
                case SEQUENCE:
                    return GenerationStrategyType.SEQUENCE;
            }
        }

        return null;
    }
    /*
     * Creates generating element.
     * @param s the definition
     * @return generator or null
     */
    private ExternalIdValueGenerator getGenerator(ValueGenerationStrategy s) {

        if (Objects.nonNull(s)) {
            switch (s.getStrategyType()) {
            case CONCAT:
                return ValueGeneratingUtils.CONCAT_ENTITY_GENERATOR;
            case RANDOM:
                return ValueGeneratingUtils.RANDOM_ENTITY_GENERATOR;
            case CUSTOM:
                return ValueGeneratingUtils.defineEntityCustomValueGenerator(((CustomValueGenerationStrategy) s).getClassName());
            default:
                break;
            }
        }

        return null;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isDefined() {
        return validityPeriod.isDefined();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Date getFrom() {
        return validityPeriod.getFrom();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Date getTo() {
        return validityPeriod.getTo();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Granularity getGranularity() {
        return validityPeriod.getGranularity();
    }
}
